package model;

import java.util.HashMap;

import utilities.DCUtilities;
import utilities.Logger;

public class Document {
	private int id;
	private HashMap<String, Term> terms;
	private HashMap<String, Term> titleTerms;
	private HashMap<String, Category> categories;
	private HashMap<String, Category> newCategories;
	private HashMap<String, Integer> frequences;
	private HashMap<String, Double> tfIdf;
	private double tempDistance;

	public Document(int id) {
		this.id = id;
		this.terms = new HashMap<String, Term>();
		this.titleTerms = new HashMap<String, Term>();
		this.categories = new HashMap<String, Category>();
		this.frequences = new HashMap<String, Integer>();
		this.tfIdf = null;
		this.tempDistance = 0.0;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public int getAllTermsCount() {
		int allTermsCount = 0;
		for (Integer fr:frequences.values()){
			allTermsCount += fr;
		}
		return allTermsCount;
	}


//	public HashMap<String, Term> getTerms() {
//		return terms;
//	}

//	public void setTerms(HashMap<String, Term> terms) {
//		this.terms = terms;
//		tfIdf = null;
//	}
	
	public void addTerm(Term term, int type) {
		if (type == DCUtilities.BODY_TERM_TYPE){
			if(term != null && ! terms.containsKey(term.getName()))
				this.terms.put(term.getName(), term);
			
		} else if (type == DCUtilities.TITLE_TERM_TYPE) {
			if(term != null && ! titleTerms.containsKey(term.getName()))
				this.titleTerms.put(term.getName(), term);
		}
		incFrequence(term.getName(), type);
		tfIdf = null;
	}
	
	public void removeTerm(String name){
		this.terms.remove(name);
		this.titleTerms.remove(name);
		this.frequences.remove(name);
		tfIdf = null;
	}

	public HashMap<String, Category> getCategories() {
		return categories;
	}

	public void setCategories(HashMap<String, Category> categories) {
		this.categories = categories;
	}
	
	
//	public HashMap<String, Integer> getFrequences() {
//		return frequences;
//	}

//	public void setFrequences(HashMap<String, Integer> frequences) {
//		this.frequences = frequences;
//		tfIdf = null;
//	}

	public void addCategory(Category cat){
		if(cat != null && ! categories.containsKey(cat.getCategoryName()))
			this.categories.put(cat.getCategoryName(), cat);
	}
	
	public boolean hasTerm(Term t){
		if (this.terms.containsKey(t.getName()) || this.titleTerms.containsKey(t.getName())){
			return true;
		}
		return false;
	}
	
	public boolean hasTerm(String t){
		if (this.terms.containsKey(t) || this.titleTerms.containsKey(t)){
			return true;
		}
		return false;
	}
	
	public boolean hasCategory(Category c){
		return this.categories.containsKey(c.getCategoryName());
	}
	
	public boolean hasNewCategory(Category c){
		return this.newCategories.containsKey(c.getCategoryName());
	}
	
	public boolean isInCategory(Category c){
		return this.categories.containsKey(c.getCategoryName());
	}
	
	public boolean isInCategory(String c){
		return this.categories.containsKey(c);
	}
	
	private void incFrequence(String key, int type) {
		if(key == null)
			return;
		Integer frequence = frequences.get(key);
		if(frequence == null)
			this.frequences.put(key, new Integer(1));
		else {
			this.frequences.put(key, new Integer(frequence+1));
		}
	}
	
	
	
	public HashMap<String, Double> getTfIdf(int allDocumentsCount){
		if (tfIdf == null){
			tfIdf = new HashMap<String, Double>();
			
			for(Term term : terms.values()) {
				double tf = (double)frequences.get(term.getName())/(double)getAllTermsCount();
//				if (titleTerms.containsKey(term.getName())){
//					tf = tf*DCUtilities.BOTH_TITLE_AND_BODY_MAGNIFIER;
//				}
				int df = term.getDocuments().size();
				if (titleTerms.containsKey(term.getName())){
					tf = -tf;
				}
				tfIdf.put(term.getName(), calculateTfIdf(tf, allDocumentsCount, df));
			}
			for(Term term : titleTerms.values()) {
				if (!terms.containsKey(term.getName())){
					double tf = -(double)frequences.get(term.getName())/(double)getAllTermsCount();
//					tf = tf*DCUtilities.TITLE_TERM_MAGNIFIER;
					int df = term.getDocuments().size();
					tfIdf.put(term.getName(), calculateTfIdf(tf, allDocumentsCount, df));
				}
			}
		}
		return (HashMap<String, Double>)tfIdf.clone();
	}
	
	private Double calculateTfIdf(double tf,double N, double df) {
		double ret = tf * Math.log(N/df);
		return new Double(ret);
	}
	
	public double getTempDistance() {
		return tempDistance;
	}

	public void setTempDistance(double tempDistance) {
		this.tempDistance = tempDistance;
	}
	
	public HashMap<String, Category> getNewCategories() {
		return newCategories;
	}

	public void setNewCategories(HashMap<String, Category> newCategories) {
		this.newCategories = newCategories;
	}
	
	public void printDocumentCategories(){
		Logger.logln("Document "+getId(), DCUtilities.CLASSIFICATION_OUTPUT_FILE_KEY);
		Logger.log("\toriginal clases:\t", DCUtilities.CLASSIFICATION_OUTPUT_FILE_KEY);
		for (Category cat:getCategories().values()){
			Logger.log(cat.getCategoryName()+" ", DCUtilities.CLASSIFICATION_OUTPUT_FILE_KEY);
		}
		Logger.logln("", DCUtilities.CLASSIFICATION_OUTPUT_FILE_KEY);
		Logger.log("\tassigned classes:\t", DCUtilities.CLASSIFICATION_OUTPUT_FILE_KEY);
		for (Category cat:getNewCategories().values()){
			Logger.log(cat.getCategoryName()+" ", DCUtilities.CLASSIFICATION_OUTPUT_FILE_KEY);
		}
		Logger.logln("", DCUtilities.CLASSIFICATION_OUTPUT_FILE_KEY);
	}
	
	public int getHitCount(){
		int hitCount = 0;
		for (Category c:getNewCategories().values()){
			if (getCategories().containsKey(c.getCategoryName())){
				hitCount ++;
			}
		}
		return hitCount;
	}
	
	public int getMissCount(){
		int missCount = 0;
		for (Category c:getNewCategories().values()){
			if (!getCategories().containsKey(c.getCategoryName())){
				missCount ++;
			}
		}
		return missCount;
	}

}
